﻿using Ganss.Xss;
using Masuit.Tools.Security;
using Masuit.Tools.Systems;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp;
using System.ComponentModel;
using System.Reflection;
using Yitter.IdGenerator;
using WebService.Logger;
using WebService.Models;
using WebService.Form;
using FluentCache;
using Masuit.Tools;
using FluentCache.Microsoft.Extensions.Caching.Memory;
using WebService.ExtendedMethods;

namespace YaseiAkira.AJ.Util
{
    public static class Util
    {
        public static readonly AJLog4Logger Log4Logger = new AJLog4Logger();

        public static readonly HtmlSanitizer HTMLSANITIZER = new HtmlSanitizer();

        private static readonly string[] _fontNames = new string[] { "Consolas", "MS Gothic", "Microsoft JhengHei", "Microsoft JhengHei UI", "Microsoft JhengHei Light", "Microsoft YaHei", "Microsoft Yi Baiti", "FangSong", "SimHei", "KaiTi", "LiSu", "SimSun", "NSimSun", "SimSun-ExtB", "YouYuan", "STCaiyun", "STFangsong", "STHupo", "STKaiti", "STLiti", "STSong", "STXihei", "STXingkai", "STXinwei", "STZhongsong" };

        private static IApplicationBuilder _app;

        public static IHttpContextAccessor HttpContextAccessor;

        private static string _aesKey;
        private static string _rsaPrivateKey;
        private static string _rsaPublicKey;

        public static readonly JsonSerializerSettings JSONSERIALIZERSETTINGS = new()
        {
            DateFormatString = "yyyy-MM-dd HH:mm:ss",
            ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore,
            ContractResolver = new CamelCasePropertyNamesContractResolver(),//new DefaultContractResolver()
            NullValueHandling = Newtonsoft.Json.NullValueHandling.Include,
            //Converters = new List<JsonConverter> { new Int64ToStringConverter() },
        };

        //public static string AESKey { get { return _aesKey; } }
        //public static string RSAPrivateKey { get { return _rsaPrivateKey; } }
        //public static string RSAPublicKey { get { return _rsaPublicKey; } }

        public static ICache _memoryCache;

        private static IWebHostEnvironment _hostEnv;

        public static IWebHostEnvironment HostEnv { get { return _hostEnv; } }


        /// <summary>
        /// 创建自定义授权token
        /// </summary>
        /// <param name="id"></param>
        /// <param name="idType"></param>
        /// <param name="ticks"></param>
        /// <param name="others"></param>
        /// <returns></returns>
        public static string CreateAuthToken(long id, int idType, long ticks = 0, params string[] others)
        {
            if (ticks == 0)
            {
                ticks = DateTime.Now.Ticks;
            }
            var array = others == null
                ? new List<string>() : others.Where(p => !string.IsNullOrWhiteSpace(p)).ToList();

            var splitChar = others.Length > 0 ? "_" : "";

            return $"{id}_{idType}_{ticks}{splitChar}{string.Join("_", others)}".RSAEncrypt(_rsaPublicKey);
        }

        /// <summary>
        /// 解析自定义授权token
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public static CommonAuthUser DecryptAuthToken(string token)
        {
            var result = new CommonAuthUser
            {
                Id = 0L,
                Type = IdentityType.Customer
            };

            token = token.RSADecrypt(_rsaPrivateKey);

            if (!string.IsNullOrEmpty(token))
            {
                var array = token.Split('_');

                var ticks = array.ElementAtOrDefault(2);

                result = new CommonAuthUser
                {
                    Id = (array.ElementAtOrDefault(0)?.TryGetLong()).GetValueOrDefault(),
                    Type = (IdentityType)(array.ElementAtOrDefault(1)?.TryGetInt()).GetValueOrDefault(),
                    PId = (array.ElementAtOrDefault(3)?.TryGetInt()).GetValueOrDefault(),
                    ExpireDate = string.IsNullOrWhiteSpace(ticks) ? null : new DateTime(ticks.TryGetLong())
                };
            }

            return result;
        }

        /// <summary>
        /// 尝试从内存缓存读取数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="region"></param>
        /// <param name="sourceHandler"></param>
        /// <param name="expireMinutes"></param>
        /// <returns></returns>
        public static T TryGetOrCreateMemoryCached<T>(string key, string region, Func<T> sourceHandler, int expireMinutes = 30)
        {
            var data = _memoryCache.Get<T>(key, region);

            if (data == null || data.IsNullOrEmpty())
            {
                var value = sourceHandler();
                if (value == null)
                {
                    return default(T);
                }
                data = _memoryCache.Set(key, region, value,
                    new CacheExpiration(TimeSpan.FromMinutes(expireMinutes)));
            }

            return data.Value;
        }

        /// <summary>
        /// 删除内存缓存数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="region"></param>
        public static void RemoveMemoryCache(string key, string region)
        {
            _memoryCache.Remove(key, region);
        }

        /// <summary>
        /// 触发接口友好错误
        /// </summary>
        /// <param name="message"></param>
        /// <exception cref="APIFriendlyErrorException"></exception>
        public static void FriendlyError(string message = "发生一些错误")
        {
            throw new APIFriendlyErrorException(message);
        }

        /// <summary>
        /// 使用特定配置序列化对象
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string AJSerializeObject(object obj)
        {
            return JsonConvert.SerializeObject(obj, JSONSERIALIZERSETTINGS);
        }

        /// <summary>
        /// 获取客户端ip
        /// </summary>
        /// <returns></returns>
        public static string GetClientIp()
        {
            return HttpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
        }

        
        /// <summary>
        /// 获取其随机字符串
        /// </summary>
        /// <param name="length"></param>
        /// <param name="hasNumber"></param>
        /// <param name="hasLowerCase"></param>
        /// <param name="hasUpperCase"></param>
        /// <param name="useSpe"></param>
        /// <returns></returns>
        public static string GetRandomString(int length, bool hasNumber = true, bool hasLowerCase = true, bool hasUpperCase = false, bool useSpe = false)
        {
            var str = "";
            var r = new Random(Guid.NewGuid().GetHashCode());
            var s = "";
            if (hasNumber == true) { str += "0123456789"; }
            if (hasLowerCase == true) { str += "abcdefghijklmnopqrstuvwxyz"; }
            if (hasUpperCase == true) { str += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
            if (useSpe == true) { str += "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
            for (int i = 0; i < length; i++)
            {
                s += str.Substring(r.Next(0, str.Length - 1), 1);
            }
            return s;
        }

        

        /// <summary>
        /// 删除带http的文件url
        /// </summary>
        /// <param name="curDomain"></param>
        /// <param name="files"></param>
        public static void TryDeleteFiles(string curDomain, params string[] files)
        {
            if (files == null || !files.Any(p => !string.IsNullOrWhiteSpace(p)))
            {
                return;
            }

            for (int i = 0; i < files.Length; i++)
            {
                if (FormChecker.HTTPURLREGEX.IsMatch(files[i]))
                {
                    files[i] = files[i].Replace(curDomain, "");
                }

                try
                {
                    var local = Util.MapPath(files[i]);

                    if (File.Exists(local))
                    {
                        File.Delete(local);
                    }
                }
                catch
                {
                }
            }
        }


        /// <summary>
        /// 获取当前域名,返回 如: http://www.a.b.com 没有最后的 /
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string GetDomain(this HttpRequest request)
        {
            return $"{request.Scheme}://{request.Host}";
        }

        

        /// <summary>
        /// 判断是否是AJAX或json请求
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static bool IsAJAXOrJsonRequest(this HttpRequest request)
        {
            var jsonContentType = !string.IsNullOrWhiteSpace(request.ContentType)
                && request.ContentType.ToLower().Contains("json");

            var ajaxHeader = request.Headers.Keys.FirstOrDefault(p => p.Equals("X-Requested-With", StringComparison.OrdinalIgnoreCase));

            var isAjax = !string.IsNullOrEmpty(ajaxHeader)
                && ajaxHeader.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase);

            var acceptJson = request.Headers.Any(p => p.Key.Equals("Accept", StringComparison.OrdinalIgnoreCase) && p.Value.ToString().Contains("json", StringComparison.OrdinalIgnoreCase));

            return jsonContentType || isAjax || acceptJson;
        }


        


        /// <summary>
        /// 尝试转换json字符串为 T,如果转换失败,则直接返回 默认 T --阿吉 2021年6月18日17点02分
        /// </summary>
        /// <param name="json"></param>
        /// <returns></returns>
        public static T TryGetJSONObject<T>(string json)
        {
            try
            {
                return JsonConvert.DeserializeObject<T>(json, JSONSERIALIZERSETTINGS);
            }
            catch
            {
                return default(T);
            }
        }

        /// <summary>
        /// 尝试转换json字符串为 T
        /// </summary>
        /// <param name="json"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool TryGetJSONObject<T>(string json, out T obj)
        {
            var result = true;
            try
            {
                obj = JsonConvert.DeserializeObject<T>(json, JSONSERIALIZERSETTINGS);
            }
            catch
            {
                obj = default(T);
                result = false;
            }
            return result;
        }

        /// <summary>
        /// 尝试转换json字符串为 object
        /// </summary>
        /// <param name="json"></param>
        /// <param name="type"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static bool TryGetJSONObject(string json, Type type, out object data)
        {
            var result = true;
            try
            {
                data = JsonConvert.DeserializeObject(json, type, JSONSERIALIZERSETTINGS);
            }
            catch
            {
                data = null;
                result = false;
            }
            return result;
        }

        /// <summary>
        /// 内存流转base64字符串
        /// </summary>
        /// <param name="ms"></param>
        /// <returns></returns>
        public static string MemoryStreamToBase64String(MemoryStream ms)
        {
            return BytesToBase64String(ms.GetBuffer());
        }

        /// <summary>
        /// 字节数组转换内存流
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private static string BytesToBase64String(byte[] bytes)
        {
            return Convert.ToBase64String(bytes);
        }



        /// <summary>
        /// 把虚拟路径转换成物理路径,如果文件夹不存在,将会自动创建
        /// </summary>
        /// <param name="path"></param>
        /// <param name="createFolder">设置为true,如果文件夹不存在,将会自动创建</param>
        /// <returns></returns>
        public static string MapPath(string path, bool createFolder = true)
        {
            if (FormChecker.HTTPURLREGEX.IsMatch(path))
            {
                path = path.Replace("http://", "").Replace("https://", "");

                path = string.Join("/", path.Split('/').Skip(1).ToArray());
            }

            if (!string.IsNullOrWhiteSpace(path) && path[0] != '/')
            {
                path = $"/{path}";
            }

            path = _hostEnv.WebRootPath + path.Replace("/", "\\");

            var pathArray = path.Split('\\');

            var lastFileName = pathArray.LastOrDefault();

            if (!string.IsNullOrWhiteSpace(lastFileName) && lastFileName.Contains('.'))
            {
                pathArray = pathArray.Take(pathArray.Length - 1).ToArray();
            }

            var dir = string.Join("\\", pathArray);

            if (createFolder && !Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }

            return path;

        }

        /// <summary>
        /// 将本地绝对路径转换成虚拟路径
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string LocalToVirtualPath(string path)
        {
            return path.Replace(_hostEnv.WebRootPath, "").Replace("\\", "/");

        }

        

        /// <summary>
        /// 配置工具类,初始化日志, 内存缓存, xss中间件等
        /// </summary>
        /// <param name="app"></param>
        /// <param name="aesKey"></param>
        /// <param name="rsaPrivateKey"></param>
        /// <param name="rsaPublicKey"></param>
        /// <param name="cfgMgr"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseAJUtil(this IApplicationBuilder app,
            string aesKey, string rsaPrivateKey, string rsaPublicKey,
            ConfigurationManager cfgMgr)
        {
            _hostEnv = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
            _aesKey = aesKey;
            _rsaPrivateKey = rsaPrivateKey;
            _rsaPublicKey = rsaPublicKey;
            var loggerProvider = app.ApplicationServices.GetRequiredService<ILoggerProvider>();
            Log4Logger.CreateLogger(loggerProvider);

            _memoryCache = new FluentIMemoryCache(app.ApplicationServices.GetRequiredService<IMemoryCache>());

            HttpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();

            YitIdHelper.SetIdGenerator(new IdGeneratorOptions(1)
            {
                SeqBitLength = 8
            });


            return app;
        }
    }
}
